package com.example.sign.core;

import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.springframework.util.StringUtils;

import java.math.BigInteger;
import java.security.*;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import static com.example.sign.constant.P7Constant.*;

/**
 * @author benjamin_5
 * @Description P7密钥对生成器
 */
public class P7KeyGenerator {
    public static final String PUBLIC_KEY = "public";
    public static final String PUBLIC_CERT_KEY = "public_cert";
    public static final String PRIVATE_KEY = "private";

    /**
     * 生成密钥对
     */
    public static Map<String, String> generateKeys() throws Exception {
        return generateKeys(DN);
    }

    /**
     * 生成密钥对
     * @param dn 身份签名
     * @return
     * @throws Exception
     */
    public static Map<String, String> generateKeys(String dn) throws Exception {
        KeyPair keyPair = buildKey();
        byte[] privateKey = keyPair.getPrivate().getEncoded();
        byte[] publicKey = keyPair.getPublic().getEncoded();
        Map<String, String> keys = new HashMap<>(2);
        // 创建自签名证书
        X509Certificate cert = createSelfSignedCertificate(keyPair,dn);
        // 生成带身份签名的公钥证书，后续提供给客户
        String certStr = Base64.getEncoder().encodeToString(cert.getEncoded());
        keys.put(PUBLIC_CERT_KEY, P7_CERT_BEGIN_STR + certStr + P7_CERT_END_STR);
        // 公私钥
        keys.put(PRIVATE_KEY, Base64.getEncoder().encodeToString(privateKey));
        keys.put(PUBLIC_KEY, Base64.getEncoder().encodeToString(publicKey));
        return keys;
    }

    private static KeyPair buildKey() throws NoSuchAlgorithmException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(2048);
        return keyPairGenerator.generateKeyPair();
    }

    /**
     * 创建自签名证书
     * @param keyPair
     * @return
     * @throws Exception
     */
    private static X509Certificate createSelfSignedCertificate(KeyPair keyPair,String dn) throws Exception {
        dn = StringUtils.isEmpty(dn) ? DN : dn;
        Date startDate = new Date();
        Date endDate = new Date(startDate.getTime() + VALIDITY_MILLISECOND);
        X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(
                new X500Name(dn),
                new BigInteger(64, new SecureRandom()),
                startDate,
                endDate,
                new X500Name(dn),
                keyPair.getPublic()
        );
        ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA").build(keyPair.getPrivate());
        X509CertificateHolder certHolder = certBuilder.build(signer);
        return new JcaX509CertificateConverter().getCertificate(certHolder);
    }

    /**
     * 私钥字符串生成PrivateKey对象
     * @param privateKeyStr
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public static PrivateKey restorePrivateKeyByStr(String privateKeyStr) throws NoSuchAlgorithmException, InvalidKeySpecException {
        // 解码私钥字符串
        byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyStr);
        // 构造PKCS8EncodedKeySpec对象
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
        // 根据私钥KeySpec生成PrivateKey对象
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePrivate(pkcs8KeySpec);
    }

    /**
     * 公钥字符串生成PublicKey对象
     * @param publicKeyStr
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public static PublicKey restorePublicKeyByStr(String publicKeyStr) throws NoSuchAlgorithmException, InvalidKeySpecException {
        // 解码私钥字符串
        byte[] privateKeyBytes = Base64.getDecoder().decode(publicKeyStr);
        // 构造PKCS8EncodedKeySpec对象
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
        // 根据私钥KeySpec生成PublicKey对象
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePublic(pkcs8KeySpec);
    }


}
